package org.alex.spring.core.resolver;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.function.Function;


/**
 * 资源分析器
 */
public class ResourceResolver {
    Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * 基础包路径
     */
    private String basePackage;

    public ResourceResolver(String basePackage) {
        this.basePackage = basePackage;
    }

    /**
     * 包扫描
     * scan扫描出所有资源文件，mapper决定需要什么文件以及格式
     *
     * @param mapper 映射函数,将Resource映射成Class Name
     * @param <R>    Class Name列表
     * @return 扫描的Resource
     */
    public <R> List<R> scan(Function<Resource, R> mapper) {
        // 通过ClassLoader获取URL列表
        String basePackagePath = this.basePackage.replace(".", "/");
        String path = basePackagePath;

        try {
            List<R> collector = new ArrayList<>();
            scan0(basePackagePath, path, collector, mapper);
            return collector;
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    private <R> void scan0(String basePackage, String path, List<R> collector, Function<Resource, R> mapper) throws IOException, URISyntaxException {
        logger.atDebug().log("scan path:{}", path);
        Enumeration<URL> en = getContextClassLoader().getResources(path);

        while (en.hasMoreElements()) {
            URL url = en.nextElement();
            URI uri = url.toURI();

            // 处理uri
            String uriStr = removeTrailingSlash(uriToString(uri));

            String uriBaseStr = uriStr.substring(0, uriStr.length() - basePackage.length());

            // 解析文件
            if (uriBaseStr.startsWith("file:")) {
                uriBaseStr = uriBaseStr.substring(5);
            }

            // 解析jar
            if (uriBaseStr.startsWith("jar:")) {
                scanFile(true, uriBaseStr, jarUriToPath(basePackage, uri), collector, mapper);
            } else {
                scanFile(false, uriBaseStr, Paths.get(uri), collector, mapper);
            }
        }
    }

    /**
     * 获取jar路径
     */
    private Path jarUriToPath(String basePackage, URI jarUri) throws IOException {
        return FileSystems.newFileSystem(jarUri, Map.of()).getPath(basePackage);
    }

    <R> void scanFile(boolean isJar, String base, Path root, List<R> collector, Function<Resource, R> mapper) throws IOException {
        String baseDir = removeTrailingSlash(base);
        Files.walk(root).filter(Files::isRegularFile).forEach(file -> {
            Resource res;
            if (isJar) {
                res = new Resource(baseDir, removeLeadingSlash(file.toString()));
            } else {
                String path = file.toString();
                String name = removeLeadingSlash(path.substring(base.length() - 1));
                res = new Resource("file:" + path, name);
            }
            logger.atDebug().log("fount resource:{}", res);
            R r = mapper.apply(res);
            if (r != null) {
                collector.add(r);
            }
        });


    }

    /**
     * 移除开头TrailingSlash
     */
    private String removeLeadingSlash(String s) {
        if (s.startsWith("/") || s.startsWith("\\")) {
            s = s.substring(1);
        }
        return s;
    }

    /**
     * 移除TrailingSlash
     */
    private String removeTrailingSlash(String s) {
        if (s.startsWith("/") || s.startsWith("\\")) {
            s = s.substring(1);
        }
        return s;
    }

    private String uriToString(URI uri) {
        return URLDecoder.decode(uri.toString(), StandardCharsets.UTF_8);
    }

    /**
     * 获取类加载器
     */
    private ClassLoader getContextClassLoader() {
        ClassLoader cl = null;
        // 获取线程的类加载器
        cl = Thread.currentThread().getContextClassLoader();
        ;
        if (cl == null) {
            // 获取类的类加载器
            cl = getClass().getClassLoader();
        }
        return cl;
    }
}
